
// Copyright (C) 2009-2011 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//  
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;

using Derm.OpenGL;

namespace Derm.Render
{
	/// <summary>
	/// Shader object.
	/// </summary>
	/// <remarks>
	/// <para>
	/// A ShaderObject is a function library allowing to compose a ShaderProgram. The
	/// ShaderObject is characterized by its source code; the source code has to be
	/// compiled, before being linked with a ShaderProgram instance. A single ShaderObject
	/// could be linked with one or more ShaderProgram instances. Once linked, it doesn't
	/// have the reason to be allocated anymore (except for reuse in another program linkage).
	/// </para>
	/// <para>
	/// A ShaderObject instance has a main class, which determine the execution stage of the code,
	/// when the formed ShaderProgram is executed. The avaialable classes depends on the current
	/// OpenGL implementation; at the current state, the shader object class is determined
	/// by the enumeration <see cref="Stage"/>.
	/// </para>
	/// <para>
	/// A ShaderObject can be correctly linked only with ShaderObject of the same class. Implementing
	/// a ShaderObject on external definition of a ShaderObject with different class will result in a
	/// link error.
	/// </para>
	/// <para>
	/// This class automatically builds the source code of each ShaderObject. The generated source code
	/// has the following strings:
	/// - Standard shader header
	/// - Linked shader program preprocessor definitions (conditional symbols)
	/// - This shader source strings
	/// 
	/// The standard header is composed from standard pragmas (current shader version, debug or
	/// optimization pragmas). This source in invariant.
	/// 
	/// Shader program preprocessor definitions are supplied by the ShaderProgram holding the
	/// ShaderObject. Indeed preprocessor definitions are shared between every ShaderObject linked
	/// with the same ShaderProgram. This source changes depending on shader program linkage.
	/// 
	/// A ShaderObject source is determined completly by derived implementation. The source could
	/// be hardcoded, defined at runtime or loaded from external storage.
	/// </para>
	/// </remarks>
	public class ShaderObject : RenderResource
	{
		#region Constructors

		/// <summary>
		/// Shader object stages.
		/// </summary>
		public enum Stage
		{
			/// <summary>
			/// Shader object is linkable at vertex stage.
			/// </summary>
			Vertex = Gl.VERTEX_SHADER,
			/// <summary>
			/// Shader object is linkable at geometry stage.
			/// </summary>
			Geometry = Gl.GEOMETRY_SHADER,
			/// <summary>
			/// Shader object is linkable at tesselation control stage.
			/// </summary>
			TessControl = Gl.TESS_CONTROL_SHADER,
			/// <summary>
			/// Shader object is linkable at tesselation evaluation stage.
			/// </summary>
			TessEvaluation = Gl.TESS_EVALUATION_SHADER,
			/// <summary>
			/// Shader object is linkable at fragment stage.
			/// </summary>
			Fragment = Gl.FRAGMENT_SHADER,
		}

		/// <summary>
		/// Shader object stage.
		/// </summary>
		public Stage ObjectStage { get { return (mType); } }

		/// <summary>
		/// Shader stage (synch with Gl constants).
		/// </summary>
		private readonly Stage mType;

		/// <summary>
		/// Construct a ShaderObject defining its main class.
		/// </summary>
		/// <param name="sType">
		/// A <see cref="Stage"/> indicating the shader stage of this ShaderObject.
		/// </param>
		private ShaderObject(Stage sType)
		{
			// Store shader type
			mType = sType;
		}

		/// <summary>
		/// Construct a ShaderObject defining its main class.
		/// </summary>
		/// <param name="sType">
		/// A <see cref="Stage"/> indicating the shader stage of this ShaderObject.
		/// </param>
		/// <param name="sourcePath">
		/// A <see cref="System.String"/> that specifies the file containing the shader object source strings.
		/// </param>
		protected ShaderObject(Stage sType, string sourcePath) : this(sType)
		{
			if (sourcePath == null)
				throw new ArgumentNullException("sourcePath");
			// Store shader path (for debugging)
			mSourceFilePath = sourcePath;
		}

		/// <summary>
		/// Construct a ShaderObject defining its execution stage and the source strings.
		/// </summary>
		/// <param name="sType">
		/// A <see cref="Stage"/> indicating the shader execution stage.
		/// </param>
		/// <param name="sSource">
		/// A <see cref="IEnumerable{String}"/> that specifies the shader object source strings.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// This exception is thrown if the parameter <paramref name="sSource"/> is null.
		/// </exception>
		public ShaderObject(Stage sType, IEnumerable<string> sSource) : this(sType, String.Empty, sSource)
		{

		}

		/// <summary>
		/// Construct a ShaderObject defining its execution stage and the source strings.
		/// </summary>
		/// <param name="sType">
		/// A <see cref="Stage"/> indicating the shader execution stage.
		/// </param>
		/// <param name="sourcePath">
		/// A <see cref="System.String"/> that specifies the file containing the shader object source strings.
		/// </param>
		/// <param name="sSource">
		/// A <see cref="IEnumerable{String}"/> that specifies the shader object source strings.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// This exception is thrown if the parameter <paramref name="sSource"/> is null.
		/// </exception>
		public ShaderObject(Stage sType, string sourcePath, IEnumerable<string> sSource) : this(sType, sourcePath)
		{
			if (sSource == null)
				throw new ArgumentNullException("sSource");

			// Store shader source
			mSourceStrings = new List<string>(sSource);
		}

		/// <summary>
		/// Construct a ShaderObject defining its main class and the source strings.
		/// </summary>
		/// <param name="sType">
		/// A <see cref="Stage"/> indicating the shader class.
		/// </param>
		/// <param name="sourceUri">
		/// A <see cref="System.Uri"/> that specifies the file containing the shader object source strings.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// This exception is thrown if the parameter <paramref name="sourceUri"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// This exception is thrown if the parameter <paramref name="sourceUri"/> doesn't point to a file.
		/// </exception>
		public ShaderObject(Stage sType, Uri sourceUri) : this(sType)
		{
			if (sourceUri == null)
				throw new ArgumentNullException("sourceUri");
			if (sourceUri.IsFile == false)
				throw new ArgumentException("not a file", "sourceUri");

			// Store shader source
			mSourceStrings = new List<string>(64);
			AppendSourceStrings(mSourceStrings, sourceUri.AbsolutePath);
		}

		#endregion

		#region Source Generation

		/// <summary>
		/// Shader object source strings.
		/// </summary>
		/// <remarks>
		/// This property is fundamental for shader object source code. It is meant as the text lines
		/// composing the shader source, without the the following elements:
		/// - version directive
		/// - pragma {optimization|debug} directives
		/// The elements listed above are appended automatically when the shader source is generated.
		/// </remarks>
		protected virtual string[] Source
		{
			get {
				return ((mSourceStrings != null) ? mSourceStrings.ToArray() : null);
			}
		}

		/// <summary>
		/// Generate ShaderObject source.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for the compilation process.
		/// </param>
		/// <param name="cctx">
		/// A <see cref="ShaderCompilerContext"/> that specifies the information required for compiling this ShaderObject.
		/// </param>
		/// <returns>
		/// It returns a <see cref="List&lt;String&gt;"/> which represent this ShaderObject source. This
		/// source text is ready to be compiled.
		/// </returns>
		private List<string> GenerateSource(RenderContext ctx, ShaderCompilerContext cctx)
		{
			List<string> shaderSource = new List<string>(256);
			string[] shaderSourceStrings = Source;

			if (ctx == null)
				throw new ArgumentNullException("ctx");
			if (cctx == null)
				throw new ArgumentNullException("cctx");

			if (shaderSourceStrings != null) {

				// Append imposed header - Every source shall compile with this header
				AppendHeader(ctx, shaderSource, RenderContext.GetGLSLVersionId(cctx.ShaderVersion));

				// Append required #define statments
				if (cctx.Defines != null) {
					foreach (string def in cctx.Defines)
						shaderSource.Add("#define " + def + "\n");
				}
				// Make compiler error reference the correct line number
				shaderSource.Add("#line 0 0\n");

				// Append specific source for composing shader essence
				AppendSourceStrings(shaderSource, shaderSourceStrings);

				// Manage #include preprocessor directives
				shaderSource = ShaderIncludeTree.ProcessIncludeDirectives(ctx, cctx, shaderSource);

				return (shaderSource);
			} else
				return (null);
		}

		/// <summary>
		/// Append default source header.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for the compilation process.
		/// </param>
		/// <param name="sLines">
		/// A <see cref="List{String}"/> which represent the current shader object
		/// source lines.
		/// </param>
		/// <param name="sVersion">
		/// A <see cref="System.Int32"/> representing the shader language version to use in generated shader.
		/// </param>
		protected static void AppendHeader(RenderContext ctx, List<string> sLines, int sVersion)
		{
			// Prepend required shader version
			if (sVersion >= 140) {
				sLines.Add("#version " + sVersion + " core\n");
			} else {
				sLines.Add("#version " + sVersion + "\n");
			}
			sLines.Add("// Pragmas directives\n");
			
			if (ctx.DebugEnabled == true) {
				// Debug directives
				sLines.Add("#pragma optimization(off)\n");
				sLines.Add("#pragma debug(on)\n");
			} else {
				sLines.Add("#pragma optimization(on)\n");
				sLines.Add("#pragma debug(off)\n");
			}
			sLines.Add("\n");
		}

		/// <summary>
		/// Append a constant array of strings.
		/// </summary>
		/// <param name="sLines">
		/// A <see cref="List&lt;String&gt;"/> representing the current shader source.
		/// </param>
		/// <param name="source">
		/// An array of <see cref="System.String"/> which represents the source for be appended
		/// at the end of the source <paramref name="sLines"/>.
		/// </param>
		/// <remarks>
		/// This is a simple utility routine will appends an array of strings to a
		/// source line list.
		/// Note that this routine automatically add the end-of-line character for each
		/// string present in <paramref name="source"/>.
		/// </remarks>
		protected void AppendSourceStrings(List<string> sLines, IEnumerable<string> source)
		{
			Debug.Assert(sLines != null);
			Debug.Assert(source != null);

			foreach (string line in source) {
				// Ensure line ending
				if (line.EndsWith("\n") == false)
					sLines.Add(line + "\n");
				else
					sLines.Add(line);
			}
		}

		/// <summary>
		/// Append every line stored in a file.
		/// </summary>
		/// <param name="sLines">
		/// A <see cref="List&lt;String&gt;"/> representing the current shader source.
		/// </param>
		/// <param name="sPath">
		/// A <see cref="System.String"/> which specify the path for accessing to the
		/// file containing the source to append.
		/// </param>
		protected void AppendSourceStrings(List<string> sLines, string sPath)
		{
			Debug.Assert(sLines != null);
			Debug.Assert(sPath != null);

			// Open file at specified path
			using (StreamReader sr = new StreamReader(sPath)) {
				// Read all text file lines
				while (sr.EndOfStream == false)
					sLines.Add(sr.ReadLine() + "\n");
				// Close file
				sr.Close();
			}
			// Stores file path for debugging
			mSourceFilePath = sPath;
		}

		/// <summary>
		/// Shader object source strings.
		/// </summary>
		/// <remarks>
		/// The strings specified using this member compose the partial source to be
		/// compiled. 
		/// </remarks>
		private readonly List<string> mSourceStrings;

		/// <summary>
		/// Shader source file path, if any.
		/// </summary>
		private string mSourceFilePath;

		#endregion

		#region Source Code Compilation

		/// <summary>
		/// Compile this ShaderObject.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for the compilation process.
		/// </param>
		/// <param name="cctx">
		/// A <see cref="ShaderCompilerContext"/> that specifies the information required for compiling this ShaderObject.
		/// </param>
		/// <remarks>
		/// This routine is called automatically by attached ShaderProgram(s), whenever
		/// it's needed.
		/// </remarks>
		/// <exception cref="ShaderException">
		/// This exception is thrown in the case this shader object is not compilable.
		/// </exception>
		private void Compile(RenderContext ctx, ShaderCompilerContext cctx)
		{
			if (ctx == null)
				throw new ArgumentNullException("ctx");
			if (cctx == null)
				throw new ArgumentNullException("cctx");

			List<string> source = GenerateSource(ctx, cctx);		// Source generation!

			if (source != null) {
				int[] mSourceLengths;
				int cStatus;

				// Set shader source
				mSourceLengths = new int[source.Count];
				for (int i = 0; i < source.Count; i++)
					mSourceLengths[i] = source[i].Length;
				Gl.ShaderSource(ObjectName, source.Count, source.ToArray(), mSourceLengths);
				RenderException.DebugCheckErrors();
				if (ctx.Caps.ShaderInclude.Supported == true) {
					string[] includePaths = new string[cctx.Includes.Count];

					cctx.Includes.CopyTo(includePaths, 0);
					// Compile shader object (specifying include paths)
					Gl.CompileShaderIncludeARB(ObjectName, includePaths.Length, includePaths, (int[])null);
					RenderException.DebugCheckErrors();
				} else {
					// Compile shader object (includes are already preprocessed)
					Gl.CompileShaderARB(ObjectName);
					RenderException.DebugCheckErrors();
				}
				// Check for compilation errors
				Gl.GetObjectParameterARB(ObjectName, Gl.COMPILE_STATUS, out cStatus);
				RenderException.DebugCheckErrors();
				if (cStatus != Gl.TRUE) {
					const int MaxInfoLength = 64 * 1024;

					StringBuilder logInfo = new StringBuilder(MaxInfoLength);
					int logLength;

					// Obtain compilation log
					Gl.GetInfoLogARB(ObjectName, MaxInfoLength - 1, out logLength, logInfo);
					RenderException.DebugCheckErrors();

					// Stop compilation process
					StringBuilder sb = new StringBuilder(logInfo.Capacity);

					string[] compilerLogLines = logInfo.ToString().Split(new char[] {'\n'}, StringSplitOptions.RemoveEmptyEntries);
					foreach (string logLine in compilerLogLines)
						sb.AppendFormat("  {0}: {1}\n", mSourceFilePath, logLine);

					sLog.Error("Shader object \"{0}\" compilation failed: {1}", mSourceFilePath ?? "<Hardcoded>", sb.ToString());
					sLog.Debug("Generate source code for shader \"{0}\"", mSourceFilePath ?? "<hardcoded>");
					LogSourceStrings(source);
					sLog.Debug("EOF");

					throw new ShaderException("shader object is not valid. Compiler output for {0}: {1}\n", mSourceFilePath, sb.ToString());
				}
			}
		}

		#endregion

		#region Shader Object Creation

		/// <summary>
		/// Create this ShaderObject.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for creating this object.
		/// </param>
		/// <param name="cctx">
		/// A <see cref="ShaderCompilerContext"/> that specify compiler parameters.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown if <paramref name="ctx"/> is null.
		/// </exception>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown if <paramref name="cctx"/> is null.
		/// </exception>
		public virtual void Create(RenderContext ctx, ShaderCompilerContext cctx)
		{
			if (cctx == null)
				throw new ArgumentNullException("cctx");

			// Cache compilation parameters (used by CreateObject)
			mCompilationParams = cctx;
			// Base implementation
			base.Create(ctx);
		}

		#endregion

		#region Shader Objects Library Support

		/// <summary>
		/// Obtain the shader library identifier using reflection.
		/// </summary>
		/// <param name="type">
		/// A <see cref="System.Type"/> that specifies a ShaderObject subclass.
		/// </param>
		/// <returns>
		/// It returns a <see cref="System.String"/> that identify the ShaderObject class in the shader library.
		/// </returns>
		internal static string GetLibraryId(Type type)
		{
			const string LibraryIdFieldName = "LibraryId";

			FieldInfo field;

			if (type == null)
				throw new ArgumentNullException("type");
			if (type.IsSubclassOf(typeof(ShaderObject)) == false)
				throw new ArgumentException("not subclassing ShaderObject", "type");

			// Get field
			if ((field = type.GetField(LibraryIdFieldName, BindingFlags.Static | BindingFlags.Public)) == null)
				throw new ArgumentException(String.Format("static field {0}.{1} not implemented", type.FullName, LibraryIdFieldName), "type");
			if (field.FieldType != typeof(string))
				throw new ArgumentException(String.Format("field {0}.{1} is not a string", type.FullName, LibraryIdFieldName), "type");

			return ((string)field.GetValue(null));
		}

		/// <summary>
		/// Determine whether a ShaderObject subclass declares default constructor stage.
		/// </summary>
		/// <param name="type">
		/// A <see cref="System.Type"/> that specifies a ShaderObject subclass.
		/// </param>
		/// <returns>
		/// It returns a boolean value indicating whether <paramref name="type"/> declares a fixes <see cref="Stage"/>. If true,
		/// the method <see cref="GetLibraryStage"/> shall not throw any exception. The value returned by <i>GetLibraryStage</i>
		/// will define the <see cref="ObjectStage"/> value once the instance created by the default constructor.
		/// </returns>
		/// <seealso cref="GetLibraryStage"/>
		/// <seealso cref="HasDefaultConstructor"/>
		internal static bool HasLibraryStage(Type type)
		{
			FieldInfo field;

			return (HasLibraryStage(type, out field));
		}

		/// <summary>
		/// Determine whether a ShaderObject subclass implements the default constructor.
		/// </summary>
		/// <param name="type">
		/// A <see cref="System.Type"/> that specifies a ShaderObject subclass.
		/// </param>
		/// <returns>
		/// It returns a boolean value indicating whether <paramref name="type"/> implements the default constructor.
		/// </returns>
		/// <remarks>
		/// The instance created by the default constructor shall have the property <see cref="ObjectStage"/> equals to
		/// the value returned by <see cref="GetLibraryStage"/> (if any).
		/// </remarks>
		/// <seealso cref="GetLibraryStage"/>
		internal static bool HasDefaultConstructor(Type type)
		{
			if (type == null)
				throw new ArgumentNullException("type");
			if (type.IsSubclassOf(typeof(ShaderObject)) == false)
				throw new ArgumentException("not subclassing ShaderObject", "type");

			return (type.GetConstructor(Type.EmptyTypes) != null);
		}

		/// <summary>
		/// Determine whether a ShaderObject subclass implements the constructor that specify.
		/// </summary>
		/// <param name="type">
		/// A <see cref="System.Type"/> that specifies a ShaderObject subclass.
		/// </param>
		/// <returns>
		/// It returns a boolean value indicating whether <paramref name="type"/> implements the constructor
		/// taking a <see cref="Stage"/> argument.
		/// </returns>
		internal static bool HasStageConstructor(Type type)
		{
			if (type == null)
				throw new ArgumentNullException("type");
			if (type.IsSubclassOf(typeof(ShaderObject)) == false)
				throw new ArgumentException("not subclassing ShaderObject", "type");

			return (type.GetConstructor(new Type[] { typeof(Stage) }) != null);
		}

		internal static Stage GetLibraryStage(Type sType)
		{
			FieldInfo field;

			if (HasLibraryStage(sType, out field) == true)
				return ((Stage)field.GetValue(null));
			else
				throw new ArgumentException(String.Format("not specifing library stage"));
			
		}

		private static bool HasLibraryStage(Type type, out FieldInfo field)
		{
			const string LibraryStageFieldName = "LibraryStage";

			// No field
			field = null;

			if (type == null)
				throw new ArgumentNullException("type");
			if (type.IsSubclassOf(typeof(ShaderObject)) == false)
				throw new ArgumentException("not subclassing ShaderObject", "type");

			if ((field = type.GetField(LibraryStageFieldName, BindingFlags.Static | BindingFlags.Public)) == null)
				return (false);
			if (field.FieldType != typeof(Stage))
				return (false);

			return (true);
		}

		/// <summary>
		/// Determine an unique identifier that specify the compiled shader object.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used to determine the shader object name space.
		/// </param>
		/// <param name="cctx">
		/// A <see cref="ShaderCompilerContext"/> determining the compiler parameteres.
		/// </param>
		/// <param name="libraryId">
		/// A <see cref="System.String"/> that identifies the shader object in library.
		/// </param>
		/// <param name="sObjectStage">
		/// A <see cref="ShaderObject.Stage"/> that specifies the shader object stage.
		/// </param>
		/// <returns>
		/// It returns a string that identify the a shader object classified with <paramref name="libraryId"/> compiled using
		/// <paramref name="ctx"/>, by specifying <paramref name="cctx"/> as compiled parameters, for the shader stage <paramref name="sObjectStage"/>.
		/// </returns>
		internal static string ComputeCompilerHash(RenderContext ctx, ShaderCompilerContext cctx, string libraryId, Stage sObjectStage)
		{
			StringBuilder hashMessage = new StringBuilder();

			if (ctx == null)
				throw new ArgumentNullException("ctx");
			if (cctx == null)
				throw new ArgumentNullException("cctx");
			if (libraryId == null)
				throw new ArgumentNullException("libraryId");

			// Take into account the render context object name space
			hashMessage.Append(ctx.ObjectNameSpace.ToString());
			// Take into account the shader object library identifier
			hashMessage.Append(libraryId);
			// Take into account the shader object library stage
			hashMessage.Append(sObjectStage.ToString());
			// Take into account the shader version
			hashMessage.Append(cctx.ShaderVersion);
			// Take into account the shader program compilation symbols
			foreach (String define in cctx.Defines)
				hashMessage.AppendFormat("{0}", define);
			// Take into account the shader program include paths
			foreach (string includePath in cctx.Includes)
				hashMessage.AppendFormat("{0}", includePath);

			// Hash all information
			byte[] hashBytes;

			using (HashAlgorithm hashAlgorithm = HashAlgorithm.Create("SHA256")) {
				hashBytes = hashAlgorithm.ComputeHash(Encoding.ASCII.GetBytes(hashMessage.ToString()));
			}

			// Convert has to string
			return (Convert.ToBase64String(hashBytes));
		}

		#endregion

		#region Logging

		/// <summary>
		/// Log a shader source code.
		/// </summary>
		/// <param name="source">
		/// 
		/// </param>
		private static void LogSourceStrings(IEnumerable<string> source)
		{
			uint lineIndex = 0;

			foreach (string sourceLines in source) {
				string[] lines = sourceLines.Split(new char[] {'\n'});

				for (int i = 0; i < lines.Length; i++) {

					if ((lines[i] == "") && ((i > 0) && (lines[i-1] != "")))
						continue;

					sLog.Debug("{0}\t{1}", lineIndex++, lines[i]);
				}
			}
		}

		/// <summary>
		/// Logger of this class.
		/// </summary>
		private static readonly ILogger sLog = Log.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

		#endregion

		#region RenderResource Overrides

		/// <summary>
		/// Shader object class.
		/// </summary>
		internal static readonly Guid ShaderObjectClass = new Guid("56F66A42-10E0-4317-AF3E-8C6EB05A9A7A");

		/// <summary>
		/// Shader object class.
		/// </summary>
		public override Guid ObjectClass { get { return (ShaderObjectClass); } }

		/// <summary>
		/// Determine whether this ShaderObject really exists for a specific context.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> that would have created (or a sharing one) the object. This context shall be current to
		/// the calling thread.
		/// </param>
		/// <returns>
		/// It returns a boolean value indicating whether this ShaderObject exists in the object space of <paramref name="ctx"/>.
		/// </returns>
		/// <remarks>
		/// <para>
		/// The object existence is done by checking a valid object by its name <see cref="IRenderResource.ObjectName"/>. This routine will test whether
		/// <paramref name="ctx"/> has created this ShaderObject (or is sharing with the creator).
		/// </para>
		/// </remarks>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown if <paramref name="ctx"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="ctx"/> is not current to the calling thread.
		/// </exception>
		public override bool Exists(RenderContext ctx)
		{
			if (ctx == null)
				throw new ArgumentNullException("ctx");
			if (ctx.IsCurrent() == false)
				throw new ArgumentException("not current", "ctx");
			
			// Object name space test (and 'ctx' sanity checks)
			if (base.Exists(ctx) == false)
				return (false);

			return (Gl.IsShader(ObjectName));
		}

		/// <summary>
		/// Create this ShaderObject.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for creating this object.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown if <paramref name="ctx"/> is null.
		/// </exception>
		public override void Create(RenderContext ctx)
		{
			// Create default compilation
			mCompilationParams = new ShaderCompilerContext();
			// Base implementation
			base.Create(ctx);
		}

		/// <summary>
		/// Create a ShaderObject name.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for creating this object name.
		/// </param>
		/// <returns>
		/// It returns a valid object name for this ShaderObject.
		/// </returns>
		protected override uint CreateName(RenderContext ctx)
		{
			// Create shader
			uint name = Gl.CreateShader((int)mType);
			RenderException.CheckErrors();

			return (name);
		}

		/// <summary>
		/// Actually create this ShaderObject resources.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for allocating resources.
		/// </param>
		protected override void CreateObject(RenderContext ctx)
		{
			// Compile this shader object
			Compile(ctx, mCompilationParams);
		}

		/// <summary>
		/// Delete a ShaderObject name.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for deleting this object name.
		/// </param>
		/// <param name="name">
		/// A <see cref="System.UInt32"/> that specifies the object name to delete.
		/// </param>
		protected override void DeleteName(RenderContext ctx, uint name)
		{
			// Delete shader object
			Gl.DeleteShader(name);
			RenderException.DebugCheckErrors();
		}

		/// <summary>
		/// ShaderCompilerContext used for compilation.
		/// </summary>
		private ShaderCompilerContext mCompilationParams;

		#endregion
	}
}
